package com.zhanglijie.improve.aqs.mix.stampedLock.demo;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.StampedLock;

import com.zhanglijie.improve.aqs.mix.stampedLock.MyStampedLock;

/**
 * @author zhanglijie
 * @version 1.0
 * @since 1.1.0 2022/10/30 0030 0:56
 * stampedLock特征演示
 *  stampedLock特性：
 *  1.读读不互斥
 *  2.写写互斥（写不能重入）
 *  3.读写互斥 亵渎互斥
 *  4.使用不当会死锁
 *  5.写锁不可重入 读锁可以重入
 *  6.reentrantReadWriteLock支持公平锁和非公平锁，stamped只支持非公平锁
 *  7.stampedlock支持乐观锁
 */
public class StampedLockDemo {

    public static void main(String[] args) throws BrokenBarrierException, InterruptedException {
       StampedLockDemo stampedLockDemo = new StampedLockDemo();
        //stampedLockDemo.readRead();
        //stampedLockDemo.writeWrite();
       // stampedLockDemo.readOrWrite();
     //  stampedLockDemo.deadLock();
       //stampedLockDemo.chongRu();
       //stampedLockDemo.fairNoFair();
       // stampedLockDemo.leguanLock();
       // stampedLockDemo.convertWriteLock();
        stampedLockDemo.convertToReadLock();
    }



    /**
     * 演示读读不互斥
     * @throws BrokenBarrierException
     * @throws InterruptedException
     */
    public void readRead() throws BrokenBarrierException, InterruptedException {
        StampedLock stampedLock = new StampedLock();
        Lock readLock = stampedLock.asReadLock();
        Lock writeLock = stampedLock.asWriteLock();
        CyclicBarrier cyclicBarrier = new CyclicBarrier(6);
        //读读不互斥
        Runnable run=()->{
          readLock.lock();
          try {
              //睡眠一秒模拟业务
              Thread.sleep(1000);
              System.out.println("读业务完成");
              cyclicBarrier.await();
          }catch (Exception e){
              e.printStackTrace();
          }finally {
              readLock.unlock();
          }
        };

        long start = System.currentTimeMillis();
        for (int i = 0; i < 5; i++) {
            new Thread(run).start();
        }

       cyclicBarrier.await();
        System.out.println(System.currentTimeMillis()-start);

    }


    /**
     * 演示写写互斥
     * @throws BrokenBarrierException
     * @throws InterruptedException
     */
    public void writeWrite() throws BrokenBarrierException, InterruptedException {
        StampedLock stampedLock = new StampedLock();
        Lock readLock = stampedLock.asReadLock();
        Lock writeLock = stampedLock.asWriteLock();
        CyclicBarrier cyclicBarrier = new CyclicBarrier(6);
        //写写互斥
        Runnable run=()->{
            writeLock.lock();
            try {
                //睡眠一秒模拟业务
                Thread.sleep(1000);
                System.out.println("写业务完成");

            }catch (Exception e){
                e.printStackTrace();
            }finally {
                writeLock.unlock();
                //这个await必须放在释放后 因为写写互斥
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        };

        long start = System.currentTimeMillis();
        for (int i = 0; i < 5; i++) {
            new Thread(run).start();
        }

        cyclicBarrier.await();
        System.out.println(System.currentTimeMillis()-start);

    }


    /**
     * 演示读写|写读互斥
     * @throws BrokenBarrierException
     * @throws InterruptedException
     */
    public void readOrWrite() throws BrokenBarrierException, InterruptedException {
        StampedLock stampedLock = new StampedLock();
        Lock readLock = stampedLock.asReadLock();
        Lock writeLock = stampedLock.asWriteLock();
        CyclicBarrier cyclicBarrier = new CyclicBarrier(6);
        CountDownLatch countDownLatch = new CountDownLatch(2);
        //读写|写读互斥
        new Thread(()->{
            writeLock.lock();
            try {
                //睡眠一秒模拟业务
                Thread.sleep(1000);
                System.out.println("写业务完成");
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                writeLock.unlock();
                countDownLatch.countDown();
            }
        },"write").start();

        new Thread(()->{
            readLock.lock();
            try {
                //睡眠一秒模拟业务
                Thread.sleep(1000);
                System.out.println("读业务完成");
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                readLock.unlock();
                countDownLatch.countDown();
            }
        },"read.md").start();

        long start = System.currentTimeMillis();
        countDownLatch.await();
        System.out.println(System.currentTimeMillis()-start);

    }


    /**
     * 演示死锁
     * @throws BrokenBarrierException
     * @throws InterruptedException
     */
    public void deadLock() {
        StampedLock stampedLock = new StampedLock();
        Lock readLock = stampedLock.asReadLock();
        Lock writeLock = stampedLock.asWriteLock();
        //锁降级--死锁（ReentrantReadWriteLock不会发生）
        writeLock.lock();
        System.out.println("验证死锁");
        readLock.lock();

        //锁升级如下面也是死锁
        // readLock.lock();
        // System.out.println("验证死锁");
        // writeLock.lock();

    }


    /**
     * 演示写锁不可以重入读锁可以重入
     * @throws BrokenBarrierException
     * @throws InterruptedException
     */
    public void chongRu() {
        StampedLock stampedLock = new StampedLock();
        Lock readLock = stampedLock.asReadLock();
        Lock writeLock = stampedLock.asWriteLock();

        //读锁可以重入
        readLock.lock();
        System.out.println("读锁可以重入 因为程序能正常退出");
        readLock.lock();
        System.out.println("读锁重入次数是 "+stampedLock.getReadLockCount());//这个方法是获得读锁总个数 由于当前就一个线程所以意义就是重入次数


        //写锁不能重入
        // writeLock.lock();
        // System.out.println("写锁如果能重入程序会执行完毕不会阻塞");
        // writeLock.lock();
    }

    /**
     * stampedLock和ReentrantReadWriteLock在公平和非公平性的差异
     * (StampedLock只支持非公平锁 而读写锁都支持）
     */
    public void fairNoFair(){
        ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(false);
       // ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(true);
       ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();


        //StampedLock stampedLock = new StampedLock();
      //  Lock writeLock = stampedLock.asWriteLock();
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
        new Thread(()->{
            writeLock.lock();
            try {
                System.out.println("业务执行");
                Thread.sleep(5000);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                writeLock.unlock();
                try {
                    cyclicBarrier.await();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

        //排队线程
        for (int i = 0; i < 4; i++) {
            new Thread(()->{
                writeLock.lock();
                try {
                    System.out.println("阶段一，排队线程 === 》业务执行");
                }finally {
                    writeLock.unlock();
                }
            }).start();
        }

        //可能抢排队非公平 也可能去排在阶段一线程后面的公平性线程
        for (int i = 0; i < 4; i++) {
            new Thread(()->{
                try {
                    cyclicBarrier.await();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                writeLock.lock();
                try {
                    System.out.println("阶段二，可能去抢看公平非公平特征支持 === 》业务执行");
                }finally {
                    writeLock.unlock();
                }
            }).start();
        }
    }

    /**
     * stampedLock乐观锁特性和ReentrantReadWriteLock读写性能比较
     * 读多写少情况
     */
    static volatile int data = 0;
    public void leguanLock() throws InterruptedException {

        ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
        ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        StampedLock stampedLock = new StampedLock();
        CountDownLatch countDownLatch = new CountDownLatch(11);
        long start = System.currentTimeMillis();

        // //10个读线程
        // for (int i = 0; i < 10; i++) {
        //     new Thread(()->{
        //         for (int i1 = 0; i1 < 1000000; i1++) {
        //          try {
        //                 readLock.lock();
        //                 int b = data;
        //             }finally {
        //                 readLock.unlock();
        //             }
        //         }
        //         countDownLatch.countDown();
        //     }).start();
        // }
        //
        // //一个写线程
        // new Thread(()->{
        //     for (int i = 0; i < 1000000; i++) {
        //          try{
        //             writeLock.lock();
        //             data = i;
        //
        //          }finally {
        //         writeLock.unlock();
        //          }
        //     }
        //     countDownLatch.countDown();
        // }).start();



        //10个读线程
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                for (int i1 = 0; i1 < 1000000; i1++) {
                        long version = stampedLock.tryOptimisticRead();
                        int b = data;
                        if(!stampedLock.validate(version)){
                            long read = stampedLock.readLock();
                            try{
                                 b = data;
                            }finally {
                                stampedLock.unlock(read);
                            }
                        }
                }
                countDownLatch.countDown();
            }).start();
        }

        //一个写线程(只有一个线程）
        new Thread(()->{
            for (int i = 0; i < 1000000; i++) {
                long write = 0;
                try{
                    write = stampedLock.writeLock();
                    data = i;
                }finally {
                    stampedLock.unlock(write);
                }
            }
            countDownLatch.countDown();
        }).start();

        countDownLatch.await();
        System.out.println(System.currentTimeMillis()-start);
    }

    /**
     * 基于全局锁而不是读锁或者写锁的转换  StampedLock stampedLock = new StampedLock(); not  stampedLock.asWriteLock() || stampedLock.asReadLock()
     * 本质想去获得写锁（什么情况下可以转换为写锁）：
     * 1.当前是读锁（只获得一次的读锁） 获得写锁 可以合并读释放写获得  tryConvertToWriteLock（readStamp) 本质上还是释放读获得写
     * 2.当前是无锁 直接获得写锁 statmpedLock.writeLock();
     * 3.当前是写锁(写锁不支持重入所以不能两次writeLock 不会出现读锁情况） 想获得写锁  用老的stampe  stamp1 = statmplock.writeLock(); stamp2 = statmplock.tryConvertToWriteLock本质释放老的写锁 用老的statmp获得新的写锁.因此stamp1==stamp2
     *  转换过程只针对一次获得的前提 如果stamlock.read(); stamplock.read();==> stamped.tryConvertToWriteLock(stamp);失败
     */
    public void convertWriteLock(){
       //  StampedLock stampedLock = new StampedLock();
       //  long stamp = stampedLock.readLock();
        ////stamp = stampedLock.readLock(); 不能连续两次  只能一次
       //  System.out.println("1.成功获得读锁stamp"+stamp);
       // try {
       //     long res = stampedLock.tryConvertToWriteLock(stamp);
       //     if (res == 0) {//转换失败
       //         System.out.println("2.转换失败 需要手动结局");
       //         stampedLock.unlockRead(stamp);
       //         stamp = stampedLock.writeLock();
       //     } else {
        //         stamp = res;
        //         System.out.println("3.成功将读锁转换位写锁new Stamp"+stamp);
       //     }
       //
       //     //业务处理
       //     Thread.sleep(200);
       // } catch (InterruptedException e) {
       //     e.printStackTrace();
       // } finally {
       //     System.out.println("4.释放写锁");
       //     stampedLock.unlockWrite(stamp);
       // }


        StampedLock stampedLock = new StampedLock();

        long stamp = 256;//无锁状态stamp的值
        System.out.println("1.无锁状态stamp"+stamp);
        try {
            long res = stampedLock.tryConvertToWriteLock(stamp);
            if (res == 0) {//转换失败
                System.out.println("2.转换失败 需要手动结局");
                stamp = stampedLock.writeLock();
            } else {
                stamp = res;
                System.out.println("3.成功将读无转换位写锁new Stamp"+stamp);
            }

            //业务处理
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("4.释放写锁");
            stampedLock.unlockWrite(stamp);
        }


       //  StampedLock stampedLock = new StampedLock();
       //  long stamp = stampedLock.writeLock();
       //  System.out.println("1.成功获得写锁stamp"+stamp);
       //  try {
       //      long res = stampedLock.tryConvertToWriteLock(stamp);
       //      if (res == 0) {//转换失败
       //          System.out.println("2.转换失败 需要手动结局");
       //          stampedLock.unlockWrite(stamp);
       //          stamp = stampedLock.writeLock();
       //      } else {
        //          stamp = res;
        //          System.out.println("3.成功将写锁转换位写锁new Stamp"+stamp);
       //      }
       //
       //      //业务处理
       //      Thread.sleep(200);
       //  } catch (InterruptedException e) {
       //      e.printStackTrace();
       //  } finally {
       //      System.out.println("4.释放写锁");
       //      stampedLock.unlockWrite(stamp);
       //  }
    }

    /**
     * 什么情况下可以转换为读锁
     * 1.当前无锁状态（就是乐观锁状态，可以加读锁，这里需要注意，加读锁的过程中，读锁是可以变化的（因为当前线程转换读锁过程中其他线程也是可以来获得读锁的读读不互斥）
     * 2.本身就是读锁（先减读锁 再加读锁 一减一加抵消）
     * 3.当前有写锁 此时可以释放这个写锁 然后再加读锁（此时可以唤醒后继节点 因为加的是读锁 读读不互斥可以唤醒后继节点）
     */
    public void convertToReadLock(){

        // StampedLock stampedLock = new StampedLock();
        // long stamp = 256;//无锁状态stamp的值
        // System.out.println("1.无锁状态stamp"+stamp);
        // try {
        //     long res = stampedLock.tryConvertToReadLock(stamp);
        //     if (res == 0) {//转换失败
        //         System.out.println("2.转换失败 需要手动结局");
        //         stamp = stampedLock.readLock();
        //     } else {
        //         stamp = res;
        //         System.out.println("3.成功将读无转换位读锁,new Stamp"+stamp);
        //     }
        //
        //     //业务处理
        //     Thread.sleep(200);
        // } catch (InterruptedException e) {
        //     e.printStackTrace();
        // } finally {
        //     System.out.println("4.释放读锁");
        //     stampedLock.unlockRead(stamp);
        // }



        // StampedLock stampedLock = new StampedLock();
        // long stamp = stampedLock.readLock();
        // System.out.println("1.获得读锁状态,stamp"+stamp);
        //
        // try {
        //     long res = stampedLock.tryConvertToReadLock(stamp);
        //     if (res == 0) {//转换失败
        //         System.out.println("2.转换失败 需要手动结局");
        //         stampedLock.unlockRead(stamp);
        //         stamp = stampedLock.readLock();
        //     } else {
        //         stamp = res;
        //         System.out.println("3.成功将读读转换位读锁,new Stamp"+stamp);
        //     }
        //
        //     //业务处理
        //     Thread.sleep(200);
        // } catch (InterruptedException e) {
        //     e.printStackTrace();
        // } finally {
        //     System.out.println("4.释放读锁");
        //     stampedLock.unlockRead(stamp);
        // }


        StampedLock stampedLock = new StampedLock();
        long stamp = stampedLock.writeLock();
        System.out.println("1.获得写锁状态,stamp"+stamp);

        try {
            long res = stampedLock.tryConvertToReadLock(stamp);
            if (res == 0) {//转换失败
                System.out.println("2.转换失败 需要手动结局");
                stampedLock.unlockWrite(stamp);
                stamp = stampedLock.readLock();
            } else {
                stamp = res;
                System.out.println("3.成功将读写转换位读锁,new Stamp"+stamp);
            }

            //业务处理
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("4.释放读锁");
            stampedLock.unlockRead(stamp);
        }
    }

    /**
     * 什么情况下可以转换为乐观锁
     * 1.当前无锁（啥都不用做，直接返回state)
     * 2.当前是写锁（写释放锁，唤醒后继节点）
     * 3.当前是读锁 可能读锁不止一个（1次只能去掉一个读锁，state-1.如果正好是一个读锁 仔state-1后还能唤醒后继节点）
     * 4.读锁>=RFULL 返回老的state的版本号
     */
    public void leGuanLock() throws InterruptedException {
        StampedLock stampedLock = new StampedLock();
        long stamp = 256;
        System.out.println("1.获得无锁状态,stamp"+stamp);
        boolean validate = false;
        try {
            long res = stampedLock.tryConvertToOptimisticRead(stamp);
            if (res == 0) {//转换失败
                System.out.println("2.转换失败 需要手动结局");
                stamp = stampedLock.tryOptimisticRead();
            } else {
                stamp = res;
                System.out.println("3.成功将读锁转换位乐观读锁,new Stamp"+stamp);
            }
            validate = stampedLock.validate(stamp);
            if(!validate){
                System.out.println("乐观锁过期了需要使用悲观读锁");
                stamp = stampedLock.readLock();//乐观失败 只能悲观了
            }
            //业务处理
            Thread.sleep(200);

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if(!validate) {
                stampedLock.unlockRead(stamp);
            }
        }

        // StampedLock stampedLock = new StampedLock();
        // long stamp = stampedLock.writeLock();
        // System.out.println("1.获得写锁状态,stamp"+stamp);
        // boolean validate = false;
        // try {
        //     long res = stampedLock.tryConvertToOptimisticRead(stamp);
        //     if (res == 0) {//转换失败
        //         System.out.println("2.转换失败 需要手动结局");
        //         stampedLock.unlockWrite(stamp);
        //         stamp = stampedLock.tryOptimisticRead();
        //     } else {
        //         stamp = res;
        //         System.out.println("3.成功将写锁转换位乐观读锁,new Stamp"+stamp);
        //     }
        //     validate = stampedLock.validate(stamp);
        //     if(!validate){
        //         System.out.println("乐观锁过期了需要使用悲观读锁");
        //         stamp = stampedLock.readLock();//乐观失败 只能悲观了
        //     }
        //     //业务处理
        //     Thread.sleep(200);
        //
        // } catch (InterruptedException e) {
        //     e.printStackTrace();
        // } finally {
        //      if(!validate) {
        //         stampedLock.unlockRead(stamp);
        //     }
        // }

        // StampedLock stampedLock = new StampedLock();
        // long stamp = stampedLock.readLock();
        // System.out.println("1.获得读锁状态,stamp"+stamp);
        // boolean validate = false;
        // try {
        //     long res = stampedLock.tryConvertToOptimisticRead(stamp);
        //     if (res == 0) {//转换失败
        //         System.out.println("2.转换失败 需要手动结局");
        //         stampedLock.unlockRead(stamp);
        //         stamp = stampedLock.tryOptimisticRead();
        //     } else {
        //         stamp = res;
        //         System.out.println("3.成功将读锁转换位乐观读锁,new Stamp"+stamp);
        //     }
        //     validate = stampedLock.validate(stamp);
        //     if(!validate){
        //         System.out.println("乐观锁过期了需要使用悲观读锁");
        //         stamp = stampedLock.readLock();//乐观失败 只能悲观了
        //     }
        //     //业务处理
        //     Thread.sleep(200);
        //
        // } catch (InterruptedException e) {
        //     e.printStackTrace();
        // } finally {
        //     if(!validate) {
        //         stampedLock.unlockRead(stamp);
        //     }
        // }

    }
}
